#!/usr/bin/env python
# encoding: utf-8

"""
@author: wuyang
@software: PyCharm
@file: dir-real-rsync.py
@time: 2017/10/25 8:23
@文件描述：监控目录，并同步有变动的文件（add,modifile,也可以设置delete）
# 需要设置同步主机的公钥，免秘钥登录。

"""
import pyinotify

import time
import os
import subprocess
import settings
import logging
import logging.config

# 初始化日志配置
logging.config.dictConfig(settings.logging_conf)
logger = logging.getLogger('common_logger')

class MonitMainHander(pyinotify.ProcessEvent):
    def __init__(self,source_path,des_ip,des_port,des_path):
        logger.debug("开始实例化MonitMainHander")
        pyinotify.ProcessEvent.__init__(self)               # 继承父类的初始化方法参数
        self.source_path = source_path                      # 被监控目录
        self.des_ip = des_ip                                # 同步的主机ip地址
        self.des_port = des_port                            # 目标主机端口
        self.des_path = des_path                            # 同步到的目标路径
        logger.debug("实例化MonitMainHander结束")

    #检查文件类型的装饰器
    def check_filetype(function):
        def filetype(self,event):
            checkfilepath = os.path.join(event.path,event.name)
            checkcommand = '/usr/bin/file -b --mime-type %s' % checkfilepath
            filetype = subprocess.check_output(checkcommand,shell=True).decode().strip("\n") # 获取返回bytes 转换为string并去掉换行
            if (filetype != 'text/plain'):
                logger.warning("发现{}文件不是纯文本文件文件类型为：{}".format(checkfilepath,filetype)) # 此处可以修改成想要的操作，例如不执行下面的方法、或者发出告警
            function(self,event)
        return filetype


    # 组装同步命令方法 这里端口写死了
    # 不同步.* 以.开头文件，一般为传输缓存文件，command命令可酌情配置，满足不不同场景需要
    # --suffix= 如有重名，需要先备份再同步，备份文件名用添加日期后缀
    def rsync_command(self,source_path,des_port,des_ip,des_path,event):
        current_time = time.strftime("%Y-%m-%d_%H:%M:%S",time.localtime())
        logger.info("有变化文件：" + os.path.join(event.path,event.name))
        command =  'rsync -ae "ssh -p %s" --timeout=60   %s %s:%s -b --exclude=.* --suffix=' % (des_port,source_path,des_ip,des_path) + current_time
        return command

    @check_filetype # 可以做文件类型的检查
    def process_IN_MODIFY(self, event): #文件发生变化的时候同步（包含了创建和修改）
        command = self.rsync_command(source_path=self.source_path,des_ip=self.des_ip,des_port=self.des_port,des_path=self.des_path,event=event)
        logger.info("组装获取到命令"+command)
        try:
            logger.info("开始执行同步命令")
            os.system(command)
            logger.info("命令执行结束")
        except Exception as e:
            logger.error(e)
    def process_IN_DELETE(self, event): # 文件发生删除操作的时候（根据情况添加）注意删除的时候可能被把备份文件也删除
        # command = self.rsync_command(source_path=self.source_path,des_ip=self.des_ip,des_port=self.des_port,des_path=self.des_path)
        # os.system(command)
        pass

# source_path:被监控目录
# des_ip: 同步目标ip地址
# des_port: 同步目标ip端口
# des_path: 同步目的路径
def do_monit(source_path,des_ip,des_path,des_port="22"):
    wm = pyinotify.WatchManager()  #create a watchmanager()
    mask = pyinotify.IN_DELETE  | pyinotify.IN_MODIFY  # 需要监控的事件（创建空的文件此处就不同步了）
    notifier = pyinotify.Notifier(wm, MonitMainHander(source_path,des_ip,des_port,des_path)) # 监控事件通知处理
    wdd = wm.add_watch(source_path, mask, rec=True)  # 加入监控，mask，rec递归
    try:
        #防止启动多个的命令 设置进程号文件就可以防止启动多个
        notifier.loop(daemonize=settings.daemonize, pid_file=settings.pid_file)
    except pyinotify.NotifierError as err:
        logger.error(err)
if __name__ == "__main__":
    do_monit(source_path=settings.source_path,des_ip=settings.des_ip,des_path=settings.des_path,des_port=settings.des_port)











